{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# The Numeric Widgets\n",
    "\n",
    "#### `Int` Widgets\n",
    "\n",
    "+ IntText\n",
    "+ BoundedIntText\n",
    "+ IntProgress\n",
    "+ IntSlider\n",
    "+ IntRangeSlider\n",
    "+ Play\n",
    "\n",
    "#### `Float` Widgets\n",
    "\n",
    "+ FloatText\n",
    "+ BoundedFloatText\n",
    "+ FloatProgress\n",
    "+ FloatSlider\n",
    "+ FloatRangeSlider\n",
    "\n",
    "----------------------\n",
    "\n",
    "First of all, we have to import the Widgets library. Sometimes we need some Haskell language extensions. We need `OverloadedStrings` because some widget methods receive a `Text` as an argument, instead of a `[Char]`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "{-# LANGUAGE OverloadedStrings #-}\n",
    "import IHaskell.Display.Widgets"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### `IntText` and `FloatText`\n",
    "We are going to see how the `Text` family works. They create a \"Stepper\", which lets us click to increment a certain amount. We can also input the number as if it were text."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "ef687ad0-ce95-43f7-8e40-45fd169f5d2b",
       "version_major": 2,
       "version_minor": 0
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "6f226f08-087f-4d38-8bae-ac7740868d57",
       "version_major": 2,
       "version_minor": 0
      }
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "intt <- mkIntText\n",
    "floatt <- mkFloatText\n",
    "intt\n",
    "floatt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For any numeric widget, we can use `getField w IntValue` and `getField w FloatValue` to obtain the value stored in the widget. Try changing the numbers in the widgets and then executing the cell bellow!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "0.0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "getField @IntValue intt\n",
    "getField @FloatValue floatt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can also use these results for any computation. For example, let us add them together:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "x <- getField @IntValue intt\n",
    "y <- getField @FloatValue floatt\n",
    "\n",
    "(fromIntegral x) + y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But incrementing and decrementing one by one is a bit boring... We can change the number incremented each step if we set the `StepInt` and `StepFloat` fields. They are of type `Maybe Integer`, so we can put `Nothing` and let the frontend decide."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "setField @StepInt intt $ Just 5\n",
    "setField @StepFloat floatt $ Just 0.5"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Try clicking on the buttons of the text field now.\n",
    "\n",
    "By the way, does this mean that we can change other fields with `setField`? Yes! For example, let us change the `IntValue`/`FloatValue`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "setField @IntValue intt 42\n",
    "setField @FloatValue floatt 3.14"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "One special field is the `ChangeHandler`. This field is an IO function that is executed everytime the value is changed. We are going to \"sync\" the two widgets using two `ChangeHandler` functions. Each time the value changes in one widget, it is changed on the other widget too. In this example, the int widget contains the doubled value of the float widget."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "ef687ad0-ce95-43f7-8e40-45fd169f5d2b",
       "version_major": 2,
       "version_minor": 0
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "6f226f08-087f-4d38-8bae-ac7740868d57",
       "version_major": 2,
       "version_minor": 0
      }
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "setField @ChangeHandler intt (getField @IntValue intt >>= setField @FloatValue floatt . (/2) . fromIntegral)\n",
    "setField @ChangeHandler floatt (getField @FloatValue floatt >>= setField @IntValue intt . (*2) . round)\n",
    "\n",
    "-- Let's display the widgets again so we don't have to scroll up and down\n",
    "intt\n",
    "floatt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### `BoundedIntText` and `BoundedFloatText`\n",
    "\n",
    "So, what's the difference between the Bounded family and the other two? `Bounded` widgets have the `Max` and `Min` attributes, which let you set an upper and lower bound. Let's try it"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "b1af0ae0-8483-418d-9927-d97a520ff23f",
       "version_major": 2,
       "version_minor": 0
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "c1a5c813-360c-4e1a-891c-291d900382d0",
       "version_major": 2,
       "version_minor": 0
      }
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "-- We create the two widgets\n",
    "bit <- mkBoundedIntText\n",
    "bft <- mkBoundedFloatText\n",
    "\n",
    "setField @MaxInt bit 5\n",
    "setField @MinInt bit (-5)\n",
    "\n",
    "setField @MaxFloat bft 20\n",
    "setField @MinFloat bft 10\n",
    "setField @StepFloat bft $ Just 0.5\n",
    "setField @FloatValue bft 15\n",
    "\n",
    "bit\n",
    "bft"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you try clicking on the buttons or editing the text, you can't but anything that is not between the `Min` and `Max`! That's pretty neat, but, if you want to do something with bounds, maybe it's better to use an slider"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### `IntSlider`, `FloatSlider`, `FloatLogSlider`, `IntRangeSlider` and `FloatRangeSlider`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "All these widgets are sliders (duh!). `IntSlider`, `FloatSlider` and `FloatLogSlider` represent a single value, whereas `IntRangeSlider` and `FloatRangeSlider` represent a pair (range) of values. `FloatLogSlider` uses a logarithmic scale, which means that every step will multiply (instead of increment) the value!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "ins <- mkIntSlider\n",
    "irs <- mkIntRangeSlider\n",
    "fns <- mkFloatSlider\n",
    "fls <- mkFloatLogSlider\n",
    "frs <- mkFloatRangeSlider\n",
    "setField @StepFloat fns $ Just 0.25\n",
    "-- We can set the base of the logslider\n",
    "setField @BaseFloat fls 2\n",
    "setField @StepFloat fls $ Just 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "07045bf2-5181-41fc-8fa9-3cdc292107e0",
       "version_major": 2,
       "version_minor": 0
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "de1fd5a4-0ded-42fe-95bc-49458f2a67da",
       "version_major": 2,
       "version_minor": 0
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "787ef5b0-bf00-4f1b-8c6d-4efc4a9323b2",
       "version_major": 2,
       "version_minor": 0
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "6832d707-3a51-479e-8860-746803312046",
       "version_major": 2,
       "version_minor": 0
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "c1b9da4e-1385-457f-a8e3-bed715536d21",
       "version_major": 2,
       "version_minor": 0
      }
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "ins\n",
    "irs\n",
    "fns\n",
    "fls\n",
    "frs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "They work the same as the previous widgets plus, they are a lot more confortable if you need to have some kind of bounds. The only difference is that with the pair ones, we have to get the `IntPairValue` and `FloatPairValue` fields"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(25,75)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "(0.0,1.0)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "getField @IntPairValue irs\n",
    "getField @FloatPairValue frs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "-- We can also set the field!\n",
    "setField @IntPairValue irs (32,42)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's create a small program using widgets that gives us the greatest common divisor of two numbers! It displays the result on a `IntText` widget"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "de1fd5a4-0ded-42fe-95bc-49458f2a67da",
       "version_major": 2,
       "version_minor": 0
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "ef687ad0-ce95-43f7-8e40-45fd169f5d2b",
       "version_major": 2,
       "version_minor": 0
      }
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "gcd a 0 = a\n",
    "gcd a b = gcd b $ a `mod` b\n",
    "\n",
    "setField @ChangeHandler irs (getField @IntPairValue irs >>= setField @IntValue intt . uncurry gcd)\n",
    "\n",
    "irs\n",
    "intt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Bonus: ContinuousUpdate**\n",
    "\n",
    "Did you realize that the value in the `IntText` widget changed while you were moving the slider? If you have a very time-consuming computation, maybe that is a very bad idea. For this, you can turn off the `ContinuousUpdate`. If you turn it off, the value will be computed only when you stop sliding the slider. But this is more easily understood if we test it!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "setField @ContinuousUpdate irs False"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Bonus: Disable**\n",
    "\n",
    "There are some widgets that can be disabled, so they appear with a different style and are not modifiable."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "setField @Disabled irs True"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Bonus: Readout**\n",
    "\n",
    "You see those little numbers at the side of the widget? They are called the `ReadOut`, you can disable it or change the format (using a format similar to printf)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "-- First we're gonna enable the widget again\n",
    "setField @Disabled irs False\n",
    "-- We add 4 trailing zeroes\n",
    "setField @ReadOutFormat irs \"04d\"\n",
    "\n",
    "-- We can disable the readout with\n",
    "\n",
    "-- setField irs ReadOut False"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### `IntProgress` and `FloatProgress`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, we have these two widgets, that we can use as progress bars."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "f3523bee-1592-4948-b76d-4c4d5582ce09",
       "version_major": 2,
       "version_minor": 0
      }
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fnp <- mkFloatProgress\n",
    "fnp"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "42.5"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "setField @FloatValue fnp 42.5\n",
    "getField @FloatValue fnp"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can also display them Vertically.\n",
    "\n",
    "*(Did you know you can also display the Sliders and other widgets vertically?)*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "setField @Orientation fnp VerticalOrientation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we are going to create a REAL progress bar! For example, let's create a thread that does some incredible complex calculations (sleeping) and then reports the progress to the widget"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "543962e4-9539-4519-b584-2aafb45cec05",
       "version_major": 2,
       "version_minor": 0
      }
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import Control.Concurrent\n",
    "import System.IO.Unsafe\n",
    "\n",
    "inp <- mkIntProgress\n",
    "inp"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [],
   "source": [
    "f :: Integer -> IO ()\n",
    "f x = do\n",
    "    threadDelay $ 10^5\n",
    "    setField @IntValue inp x\n",
    "   \n",
    "thid <- forkIO $ mapM_ f [0..100]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### The `Play` widget\n",
    "\n",
    "This widgets display a play/repeat buttons that step through values automatically (and optionally loop)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "ec00ccdc-23d7-44e6-9fc5-a58ed5134b19",
       "version_major": 2,
       "version_minor": 0
      }
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "play <- mkPlay\n",
    "setField @ChangeHandler play (getField @IntValue play >>= setField @IntValue inp)\n",
    "play"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Haskell",
   "language": "haskell",
   "name": "haskell"
  },
  "language_info": {
   "codemirror_mode": "ihaskell",
   "file_extension": ".hs",
   "mimetype": "text/x-haskell",
   "name": "haskell",
   "pygments_lexer": "Haskell",
   "version": "9.8.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
